package edu.nmt.cs.cse326.team4.crap;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import android.location.Location;
import android.util.Log;

import com.google.android.gms.maps.model.LatLng;

/** Functions to help you query the DB. */
public class DBAdapter {
	
	// TODO: move this server to a more permanent location
	public static final String url = "http://wernicke.homeftp.net/crap/";
	
	public static Map<String,String> amenities = new HashMap<String,String>() {/**
		 * 
		 */
		private static final long serialVersionUID = 1L;

	{
		put("auto_flush","Automatic flushing");
		put("auto_faucet","Automatic faucet");
		put("auto_paper", "Automatic towel dispenser");
		put("diaper_changer", "Diaper changing station");
		put("fem_product_dispenser", "Feminine product dispenser");
		put("seat_cover", "Disposable seat covers");
		}};
	public static String AUTO_FLUSH_AMENITY = "Automatic flush";
	public static String AUTO_FAUCETS_AMENITY = "Automatic faucets";
	public static String AUTO_PAPER_AMENITY = "Automatic towel dispenser";
	public static String DIAPER_CHANGER_AMENITY = "Diaper changing statio";
	public static String FEMININE_PRODUCT_AMENITY = "Feminine product dispenser";
	public static String SEAT_COVERS_AMENITY = "Seat covers";
	@SuppressWarnings({ "unused" })
	/** Compute distance between to geo-coords. */
	private static float distance(LatLng loc1, LatLng loc2) {
		float[] results = {0};
		Location.distanceBetween(loc1.latitude, loc1.longitude, loc2.latitude, loc2.longitude, results);
		
		return results[0];
		
	}
	
	/**
	 * Gets a list of toilets within distance meters of origin that has all the amenities listed.
	 * Origin is required. Distance and amenities can be null, in which case default distance is 1km.
	 * @param origin
	 * @param distance
	 * @param amenities
	 * @return
	 */
	public static ArrayList<Toilet> getNearbyToilets(LatLng origin, int distance, ArrayList<String> amenities) {
		if (distance == 0)
			distance = 1000;
		ArrayList<Toilet> nearbyToilets = new ArrayList<Toilet>();	
		List<NameValuePair> params = new ArrayList<NameValuePair>();
		params.add(new BasicNameValuePair("lat_orig",Double.toString(origin.latitude)));
		params.add(new BasicNameValuePair("lng_orig",Double.toString(origin.longitude)));
		params.add(new BasicNameValuePair("distance",Integer.toString(distance)));
		params.add(new BasicNameValuePair("debug","0"));
		if (amenities != null && amenities.size() > 0)
			params.add(new BasicNameValuePair("amenities",StringUtils.join(amenities, ",")));
		JSONObject json = post(url+"toilets/request.php",params);
		try {
			Log.d("getNearbyToilets",json.getString("message"));
			Log.d("getNearbyToilets",json.getString("toilets"));
			JSONArray results = json.getJSONArray("toilets");
			for (int i = 0; i < results.length(); i++) {
				JSONObject result = results.getJSONObject(i);
				nearbyToilets.add(new Toilet(result.getInt("id"),
						result.getString("name"),
						result.getDouble("lat"),
						result.getDouble("lng"),
						result.getString("address"),
						(float) result.getDouble("rating"),
						result.getInt("votes")));
			}
		} catch (JSONException e) {
			Log.d("getNearbyToilets","couldn't parse json response");
		}
		
		return nearbyToilets;
	}
	
	/** Log in as a user with the given password. Returns 1 on successful login, else 0. */
	public static int login(String user, String passwd) {
		if (passwd != null) {
			List<NameValuePair> params = new ArrayList<NameValuePair>();
			params.add(new BasicNameValuePair("id",user));
			params.add(new BasicNameValuePair("passwd",passwd));
			JSONObject json = post(url+"users/request.php",params);
			try {
				JSONObject result = json.getJSONArray("users").getJSONObject(0);
				Log.d("login",result.toString());
				if (result != null)
					return 1;
			} catch (JSONException e) {
				Log.d("login","couldn't parse json response");
			}
		}
		return 0;
	}
	
	/** Add a user with the given password. Returns 1 on successful add, else 0. */
	public static int addUser(String user, String passwd) {
		if (passwd != null) {
			List<NameValuePair> params = new ArrayList<NameValuePair>();
			params.add(new BasicNameValuePair("id",user));
			params.add(new BasicNameValuePair("passwd",passwd));
			JSONObject json = post(url+"users/create.php",params);
			try {
				return json.getInt("success");
			} catch (JSONException e) {
				Log.d("addUser","couldn't parse json response");
			}
		}
		return 0;
	}
		
	/** Get a toilet by location. Doesn't work. */
	public static Toilet getToilet(LatLng latLng) {
		return null;
	}
	
	/**
	 * Gets toilet by id. This is the preferred way to get a toilet.
	 * @param name
	 * @return
	 */
	public static Toilet getToilet(int id) {
		Toilet toilet = null;
		List<NameValuePair> params = new ArrayList<NameValuePair>();
		params.add(new BasicNameValuePair("id",Integer.toString(id)));
		JSONObject json = post(url+"toilets/request.php",params);
		try {
			JSONObject result;
			if (json.getInt("success") == 1 && (result = json.getJSONArray("toilets").getJSONObject(0)) != null) {
				toilet = new Toilet(result.getInt("id"),result.getString("name"),
						result.getDouble("lat"), result.getDouble("lng"), result.getString("address"),
						null, null, 0, (float) result.getDouble("rating"), result.getInt("votes"),
						null, null, null, null);
				params.add(new BasicNameValuePair("toilet",Integer.toString(id)));
				json = post(url+"amenities/request.php",params);
				JSONArray results = json.getJSONArray("amenities");
				for (int i = 0; i < results.length(); i++)
					toilet.addAmenity(results.getJSONObject(i).getString("type"));
				json = post(url+"comments/request.php",params);
				results = json.getJSONArray("comments");
				for (int i = 0; i < results.length(); i++) {
					result = results.getJSONObject(i);
					try {
						toilet.addComment(new Comment(result.getString("user"), result.getString("comment"), 
								new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").parse(result.getString("timeStamp")),
								(float)result.getDouble("rating")));
					} catch (ParseException e) {
						Log.d("getToilet","couldn't convert comment timestamp");

					}
				}
			}
		} catch (JSONException e) {
			Log.d("getToilet","couldn't parse json response");
		}
		return toilet;
	}
	
	/** Add a toilet. */
	public static void addToilet(Toilet toilet){
		List<NameValuePair> params = new ArrayList<NameValuePair>();
		params.add(new BasicNameValuePair("lat",Double.toString(toilet.getLocation().latitude)));
		params.add(new BasicNameValuePair("lng",Double.toString(toilet.getLocation().longitude)));
		params.add(new BasicNameValuePair("name",toilet.getName()));
		params.add(new BasicNameValuePair("address",toilet.getAddress()));
		params.add(new BasicNameValuePair("rating",Float.toString(toilet.getRating())));
		params.add(new BasicNameValuePair("votes","1"));
		JSONObject json = post(url+"toilets/create.php",params);
		try {
			Log.d("addToilet",json.getString("message"));
		} catch (JSONException e) {
			Log.d("addToilet","couldn't parse json response");
		}
	}

	/** Add a comment to a toilet. 
	 * @param id the toilet id
	 * @param comment the comment 
	 */
	public static void addComment(int id, Comment comment){
		List<NameValuePair> params = new ArrayList<NameValuePair>();
		params.add(new BasicNameValuePair("toilet",Integer.toString(id)));
		params.add(new BasicNameValuePair("user",comment.getAuthor()));
		params.add(new BasicNameValuePair("comment",comment.getComment()));
		params.add(new BasicNameValuePair("rating",Float.toString(comment.getRating())));
		JSONObject json = post(url+"comments/create.php",params);
		try {
			Log.d("addComment",json.getString("message"));
		} catch (JSONException e) {
			Log.d("addComment","couldn't parse json response");
		}
	}

	/** Update a toilet. Doesn't work yet. */
	public static void updateToilet(Toilet toilet) {
		//TODO: make this
	}
	
	/** Make a POST request to url with params. See getNearbyToilets() for usage example. */
	public static JSONObject post(String url, List<NameValuePair> params) {
		InputStream is = null;
		String json = "";
		JSONObject jObj = null;
		try {
			DefaultHttpClient httpClient = new DefaultHttpClient();
			HttpPost httpPost = new HttpPost(url);
			UrlEncodedFormEntity uefe = new UrlEncodedFormEntity(params);
			httpPost.setEntity(uefe);

			HttpResponse httpResponse = httpClient.execute(httpPost);
			HttpEntity httpEntity = httpResponse.getEntity();
			is = httpEntity.getContent();
		} catch (UnsupportedEncodingException e) {
			Log.e("POST",e.getMessage());
		} catch (ClientProtocolException e) {
			Log.e("POST",e.getMessage());
		} catch (IOException e) {
			Log.e("POST",e.getMessage());
		}

		try {
			BufferedReader reader = new BufferedReader(new InputStreamReader(is, "iso-8859-1"), 8);
			StringBuilder sb = new StringBuilder();
			String line = null;
			while ((line = reader.readLine()) != null) {
				sb.append(line + "\n");
			}
			is.close();
			json = sb.toString();
		} catch (Exception e) {
			Log.e("POST", "Error converting result " + e.toString());
		}

		// try parse the string to a JSON object
		try {
			jObj = new JSONObject(json);
		} catch (JSONException e) {
			Log.e("POST", "Error parsing data " + e.toString() + json);
		}

		// return JSON String
		return jObj;
		
	}
}
